#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>

// 线程信息结构体，保存线程ID和名称
struct ThreadInfo {
    pthread_t tid;        // 线程ID
    std::string name;     // 线程名称
};

// 默认线程池中线程数量
static const int defalutnum = 5;

template <class T>
class ThreadPool {
public:
    // 互斥锁操作封装
    void Lock() {
        // 对互斥锁加锁，如果锁已被占用则阻塞等待
        pthread_mutex_lock(&mutex_);  
    }

    void Unlock() {
        // 解锁互斥锁,释放锁的占用
        pthread_mutex_unlock(&mutex_);
    }

    // 条件变量操作封装
    // 唤醒一个正在等待该条件变量的线程
    void Wakeup() {
        // 唤醒一个正在等待该条件变量的线程
        // 如果有多个线程等待，则随机唤醒一个
        pthread_cond_signal(&cond_);//去任务队列唤醒
    }

    //线程休眠
    void ThreadSleep() {
        // 使当前线程在条件变量上等待
        // 1. 自动释放互斥锁mutex_
        // 2. 线程进入睡眠状态
        // 3. 当被唤醒时,自动重新获取互斥锁
        pthread_cond_wait(&cond_, &mutex_);  
        /*
        参数和返回值：
            参数1 cond：条件变量的指针
            参数2 mutex：互斥锁的指针
        返回值：成功返回0，失败返回错误码
        */
    }

    // 判断任务队列是否为空
    bool IsQueueEmpty() {
        // 返回任务队列的空状态
        return tasks_.empty();
    }

    // 根据线程ID获取线程名称
    std::string GetThreadName(pthread_t tid) {
        // 遍历线程信息数组
        for (const auto &ti : threads_) {
            // 找到匹配的线程ID
            if (ti.tid == tid)
                return ti.name;  // 返回对应的线程名
        }
        return "None";  // 未找到则返回"None"
    }

public:

    // 1.启动线程池
    void Start() {
        // 获取线程数量（线程池大小）
        int num = threads_.size();
        
        // 循环创建工作线程
        for (int i = 0; i < num; i++) {
            // 为每个线程设置名称，格式为 "thread-1", "thread-2" 等
            // std::to_string 是 C++ 标准库提供的函数，用于将数值类型转换为字符串
            threads_[i].name = "thread-" + std::to_string(i + 1);
            
            // 创建线程
            pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this);
            /*
            int pthread_create(
                pthread_t *thread,          // 输出参数，存储新创建的线程ID
                const pthread_attr_t *attr, // 线程属性，nullptr表示使用默认属性
                void *(*start_routine)(void*), // 线程函数
                void *arg                   // 传递给线程函数的参数
            );
            */
        }
    }

    // 工作线程的处理函数
    static void* HandlerTask(void* args) {
        // 将 void* 的参数args转换回线程池指针
        ThreadPool<T>* tp = static_cast<ThreadPool<T>*>(args);
        //static_cast<>用于进行编译时类型转换。
        //语法： static_cast<新类型>(表达式)
        //ThreadPool<T>*表示指向ThreadPool<T>类型对象的指针

        // 获取当前线程的名称
        // pthread_self() 返回当前线程ID
        std::string name = tp->GetThreadName(pthread_self());
        
        // 工作线程的主循环
        while (true) {
            // 先加锁：准备访问共享资源(任务队列)
            tp->Lock();
            
            // 当任务队列为空时，进入等待状态
            while (tp->IsQueueEmpty()) {
                tp->ThreadSleep();  // 条件变量等待
            }
            
            // 从任务队列取出一个任务
            T t = tp->Pop();
            
            // 取完任务后解锁：不再访问共享资源，让其他线程可以取任务
            tp->Unlock();

            // 执行任务，它调用了任务对象的 operator() 运算符，也就是实际执行任务的代码
            t();//(不需要加锁，因为任务已经安全地取出)
            
            // 输出任务执行结果
            std::cout << name << " run, " << "result: " << t.GetResult() << std::endl;
        }
    }

    // 向任务队列添加任务
    void Push(const T& t) {
        // 加锁保护任务队列
        Lock();  

        // 将任务添加到队列末尾
        tasks_.push(t);
        Wakeup();  // 唤醒一个等待的工作线程来处理新任务，使用pthread_cond_signal通知

        Unlock();  // 解锁,允许其他线程访问任务队列
    }

    // 从任务队列获取任务
    T Pop() {
        T t = tasks_.front();
        tasks_.pop();
        return t;
    }

    // 获取线程池单例
    static ThreadPool<T>* GetInstance() {
        // 第一次检查：不加锁
        if (nullptr == tp_) {  // 双检锁模式的第一次检查
            pthread_mutex_lock(&lock_);// 加锁
            
            // 第二次检查：加锁后再次验证
            if (nullptr == tp_) {  // 双检锁模式的第二次检查
                std::cout << "log: singleton create done first!" << std::endl;
                tp_ = new ThreadPool<T>();
                // 创建一个新的ThreadPool对象并返回指向该对象的指针，将新创建的ThreadPool对象的地址赋值给tp_
            }
            pthread_mutex_unlock(&lock_);// 解锁
        }
        return tp_;
        /*
        只用一次检查且不加锁：多个线程同时进入if语句，会创建多个实例。
        直接加锁：每次获取实例都要加锁，即使实例已经创建好了，性能很差。
--------------------------------------------------------------------------
 第一次调用GetInstance时：
        Thread1                    Thread2
        检查tp_为空               检查tp_为空
        获得锁                    等待锁
        再次检查tp_为空
        创建实例
        释放锁                    获得锁
                                检查tp_不为空
                                直接返回
--------------------------------------------------------------------------
后续调用GetInstance时：
        Thread1                    Thread2
        检查tp_不为空             检查tp_不为空
        直接返回                  直接返回
--------------------------------------------------------------------------
        */
    }

private:
    // 构造函数（私有）
    // 1. 声明为私有构造函数，禁止外部直接创建对象，保证单例模式
    // 2. 参数 num 指定线程池中线程的数量，默认值为 defalutnum（5个线程）
    // 3. 使用初始化列表 threads_(num) 创建指定数量的线程信息存储空间
    // 4. 初始化互斥锁和条件变量，用于任务队列的线程同步
    ThreadPool(int num = defalutnum) : threads_(num) {
        pthread_mutex_init(&mutex_, nullptr);    // 初始化互斥锁
        /*
        参数说明：
            mutex: 指向 pthread_mutex_t 类型的指针，用于存储要初始化的互斥锁
            attr: 指向 pthread_mutexattr_t 类型的指针，用于指定互斥锁的属性
                传入 nullptr 表示使用默认属性
                默认属性为：快速互斥锁（PTHREAD_MUTEX_FAST_NP）
        返回值：
            成功返回 0
            失败返回错误码
        */
        pthread_cond_init(&cond_, nullptr);      // 初始化条件变量
        /*
        参数说明：
            cond: 指向 pthread_cond_t 类型的指针，用于存储要初始化的条件变量
            attr: 指向 pthread_condattr_t 类型的指针，用于指定条件变量的属性
                传入 nullptr 表示使用默认属性
                默认属性为：进程私有（PTHREAD_PROCESS_PRIVATE）
        返回值：
            成功返回 0
            失败返回错误码
        */
    }

    // 析构函数（私有）
    // 1. 声明为私有析构函数，禁止外部直接删除对象
    // 2. 负责清理线程池资源，销毁互斥锁和条件变量
    // 3. 由于使用单例模式，析构函数通常不会被调用（程序结束时系统自动清理）
    ~ThreadPool() {
        pthread_mutex_destroy(&mutex_);    // 销毁互斥锁
        pthread_cond_destroy(&cond_);      // 销毁条件变量
    }

    // 禁止拷贝构造和赋值
    // 1. 使用 delete 关键字删除拷贝构造函数和赋值运算符
    // 2. 保证线程池对象不能被复制，维护单例模式的唯一性
    // 3. 防止因拷贝导致的资源多重释放问题
    ThreadPool(const ThreadPool<T>&) = delete;              // 禁用拷贝构造函数
    const ThreadPool<T>& operator=(const ThreadPool<T>&) = delete;  // 禁用赋值运算符

private:
    std::vector<ThreadInfo> threads_;  // 工作线程集合
    std::queue<T> tasks_;             // 任务队列

    pthread_mutex_t mutex_;           // 任务队列互斥锁
    pthread_cond_t cond_;            // 任务队列条件变量

    static ThreadPool<T>* tp_;        // 单例指针
    static pthread_mutex_t lock_;     // 单例锁
};

// 静态成员初始化
template <class T>
ThreadPool<T>* ThreadPool<T>::tp_ = nullptr;
//nullptr 将单例指针tp_初始化为空指针

template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;
//PTHREAD_MUTEX_INITIALIZER 是一个宏，用于初始化互斥锁为默认属性